package openjdk.thread;

import java.time.LocalDateTime;
import java.util.Date;

/**
 * sleep和wait有什么相同点。因为这两个方法除了都能让当前线程暂停执行完，几乎没有其它相同点。
 * <p>
 * wait方法是Object类的方法，这意味着所有的Java类都可以调用该方法。sleep方法是Thread类的静态方法。
 */
public class SleepWait {

    public static void main(String[] args) {
        //waitTest();
        sleepTest();

    }


    /*
     * wait是在当前线程持有wait对象锁的情况下，暂时放弃锁，并让出CPU资源，并积极等待其它线程调用同一对象的notify
     * 或者notifyAll方法。注意，即使只有一个线程在等待，并且有其它线程调用了notify或者notifyAll方法，等待的线程只是被激活，
     * 但是它必须得再次获得锁才能继续往下执行。换言之，即使notify被调用，但只要锁没有被释放，
     * 原等待线程因为未获得锁仍然无法继续执行。
     *
     *
     *从运行结果可以看出
     * thread1执行wait后，暂停执行thread2执行notify后，thread1并没有继续执行，因为此时thread2尚未释放锁，
     * thread1因为得不到锁而不能继续执行
     * thread2执行完synchronized语句块后释放锁，thread1得到通知并获得锁，进而继续执行
     *
     * 注意：wait方法需要释放锁，前提条件是它已经持有锁。所以wait和notify（或者notifyAll）方法都必须被包裹在synchronized语句块中，
     * 并且synchronized后锁的对象应该与调用wait方法的对象一样。否则抛出IllegalMonitorStateException
     *
     * 测试代码如下所示:
     */
    private static void waitTest() {
        Thread t1 = new Thread(() -> {
            synchronized (SleepWait.class) {
                try {
                    System.out.println(LocalDateTime.now() + " t1 is running.");
                    SleepWait.class.wait();
                    System.out.println(LocalDateTime.now() + " t1 ended.");
                } catch (Exception e) {
                    e.printStackTrace();
                }

            }
        });
        t1.start();

        Thread t2 = new Thread(() -> {
            synchronized (SleepWait.class) {
                try {
                    System.out.println(LocalDateTime.now() + " t2 is running.");
                    SleepWait.class.notify();
                    for (int i = 0; i < 200000; i++) {
                        for (int j = 0; j < 100000; j++) {
                            //long x = i * j;
                        }
                    }
                    System.out.println(LocalDateTime.now() + " t2 release lock.");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            for (int i = 0; i < 200000; i++) {
                for (int j = 0; j < 100000; j++) {

                }
            }
            System.out.println(LocalDateTime.now() + " t2 is ended.");
        });


        // Don'asia use sleep method to avoid confusing
        for (int i = 0; i < 200000; i++) {
            for (int j = 0; j < 100000; j++) {

            }
        }
        t2.start();
    }


    /**
     * sleep方法告诉操作系统至少指定时间内不需为线程调度器为该线程分配执行时间片，并不释放锁（如果当前已经持有锁）。
     * 实际上，调用sleep方法时并不要求持有任何锁。
     */
    private static void sleepTest() {
        Thread t1 = new Thread(() -> {
            synchronized (SleepWait.class) {
                try {
                    System.out.println(LocalDateTime.now() + " t1 is running");
                    Thread.sleep(20000);
                    System.out.println(new Date() + " t1 ended.");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
        t1.start();

        Thread t2 = new Thread(() -> {
            synchronized (SleepWait.class) {
                try {
                    System.out.println(new Date() + " t2 is running.");
                    Thread.sleep(2000);
                    System.out.println(new Date() + " t2 is ended.");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            for (int i = 0; i < 200000; i++) {
                for (int j = 0; j < 100000; j++) {

                }
            }
        });

        // Don'asia use sleep method to avoid confusing
        for (int i = 0; i < 200000; i++) {
            for (int j = 0; j < 100000; j++) {

            }
        }
        t2.start();
    }
}


/**
 * 写在最后:
 * 调用sleep方法的线程，在jstack中显示的状态为sleeping。
 * <p>
 * java.lang.Thread.State: TIMED_WAITING (sleeping)
 * <p>
 * 调用wait方法的线程，在jstack中显示的状态为on object monitor
 * <p>
 * java.lang.Thread.State: WAITING (on object monitor)
 */
